#include "TabelPMK.h"
#include "..\cGraphics\CGraphics.h"

// KONSTRUKTOR - DESTRUKTOR
TabelPMK	CreateEmptyTabelPMK() {
	TabelPMK T = NULL;
	return T;
}

AddressPMK	AllocateRecordPMK(AddressMhs Mhs, AddressMK mk, int Semester, ThnAkademik T, char nilai) {
	return AllocateRecordPMKAdv(Mhs, mk, Semester, nilai, T, NULL);
}

AddressPMK	AllocateRecordPMKAdv(AddressMhs Mhs, AddressMK mk, int Semester, char nilai, ThnAkademik T, AddressPMK next) {
	AddressPMK M = (AddressPMK) malloc(sizeof(RecordPMK));
	if(M!=NULL) {
		MHS(M) 		= Mhs;
		MK(M) 		= mk;
		SEMESTER(M)	= Semester;
		NILAI(M)	= nilai;
		TAHUN(M)	= T;
		NEXT(M) 	= next;
		OWNERLIST(M)= CreateEmptyOwnerList();
	}
	return M;
}

void 		DeallocateRecordPMK(RecordPMK *PMK) {
	free(PMK);
}

// PREDIKAT
void		ChangeRecordPMKContent(AddressPMK PMK, AddressMhs Mhsbaru, AddressMK MKbaru, int semesterBaru, ThnAkademik TBaru, char nilaiBaru) {
	MHS(PMK) 		= Mhsbaru;
	MK(PMK) 		= MKbaru;
	SEMESTER(PMK)	= semesterBaru;
	NILAI(PMK)		= nilaiBaru;
	TAHUN(PMK)		= TBaru;
}

int 		GetRecordPMKSize(AddressPMK PMK) {
	return NIM(MHS(PMK)).length + KODEMK(MK(PMK)).length + 2 + 4 + 1;
}

// OPERASI
void	 	AddRecordPMK(AddressPMK PMK, TabelPMK *TPMK) {
	if(ISLINKEDLISTEMPTY(*TPMK)) { // Jika kosong
		FIRST(*TPMK) = PMK;
		NEXT(PMK) = NULL;
	}
	else if(IsStringLt(NIM(MHS(PMK)),NIM(MHS(FIRST(*TPMK))))) {
		NEXT(PMK) = FIRST(*TPMK);
		FIRST(*TPMK) = PMK;
	}
	else {
		AddressPMK M;
		TRAVERSETOEND(M,FIRST(*TPMK)) 
			if(IsStringLt(NIM(MHS(PMK)),NIM(MHS(NEXT(M))))) break;
		
		NEXT(PMK) = NEXT(M);
		NEXT(M) = PMK;
	}
}

void 	RemoveRecordPMK(AddressPMK PMK, TabelPMK *TPMK) {
	if(PMK==FIRST(*TPMK)) {
		FIRST(*TPMK) = NEXT(PMK);
	}
	else {
		AddressPMK M;
		TRAVERSETOEND(M,FIRST(*TPMK))
			if(NEXT(M)==PMK) break;
		
		NEXT(M) = NEXT(NEXT(M));
	}
}

void	 	DestroyRecordPMK(AddressPMK PMK, TabelPMK *TPMK) {
	RemoveRecordPMK(PMK,TPMK);
	DestroyOwnerList(&OWNERLIST(PMK));
	DeallocateRecordPMK(PMK);
}

AddressPMK	SearchRecordPMK(AddressPMK PMK, TabelPMK *TPMK) {
	AddressPMK retval;
	if(ISLINKEDLISTEMPTY(*TPMK)) { // Jika kosong
		retval = NULL;
	}
	else if(PMK == FIRST(*TPMK)) { 
		retval = FIRST(*TPMK);
	}
	else {
		AddressPMK M;
		TRAVERSETOEND(M,FIRST(*TPMK)) 
			if(PMK == NEXT(M)) break;
		
		retval = NEXT(M);
	}
	return retval;
}

AddressPMK	SearchRecordPMKByKey(AddressMhs Mhs, AddressMK MK, int semester, ThnAkademik TA, TabelPMK *TPMK) {
	AddressPMK retval;
	if(ISLINKEDLISTEMPTY(*TPMK)) { // Jika kosong
		retval = NULL;
	}
	else if((Mhs == MHS(FIRST(*TPMK))) && (MK==MK(FIRST(*TPMK))) && (semester == SEMESTER(FIRST(*TPMK))) && (IsThnAkademikEq(TA,TAHUN(FIRST(*TPMK))))) { 
		retval = FIRST(*TPMK);
	}
	else {
		AddressPMK M;
		TRAVERSETOEND(M,FIRST(*TPMK)) 
			if((Mhs == MHS(NEXT(M))) && (MK==MK(NEXT(M))) && (semester == SEMESTER(NEXT(M))) && (IsThnAkademikEq(TA,TAHUN(NEXT(M))))) break;
		
		retval = NEXT(M);
	}
	return retval;
}

void		AddOwnerToRecordPMK(AddressPMK PMK, ownerAddress O) {
	AddOwner(&OWNERLIST(PMK),O);
}

float		GetNilaiInNumber(AddressPMK PMK) {
	switch(UpperChar(NILAI(PMK))) {
		case 'A' : return 4.0; break;
		case 'B' : return 3.0; break;
		case 'C' : return 2.0; break;
		case 'D' : return 1.0; break;
		case 'E' : return 0.0; break;
	}
}

int 		CountRecordPMK(TabelPMK T) {
	int jml = 0;
	AddressPMK M;
	TRAVERSETOEND2(M,FIRST(T)) jml++;
	return jml;
}

int			CountNodeRecordPMK(TabelPMK T, nodeAddress N) {
	int c = 0;
	AddressPMK M;
	TRAVERSETOEND2(M,FIRST(T)) 
		if(SearchOwnerByNode(&OWNERLIST(M),N)) c++;
		
	return c;
}

void		UpdateIPKdanJmlSKSRecordMhs(AddressMhs Mhs, TabelPMK *TPMK) {
	int JmlSKS = 0;
	float nilai = 0.0;
	
	if(ISLINKEDLISTEMPTY(*TPMK)) { }
	else {
		AddressPMK PMK;
		TRAVERSETOEND(PMK,FIRST(*TPMK))
			if(Mhs==MHS(PMK)) { JmlSKS+=SKS(MK(PMK)); nilai += GetNilaiInNumber(PMK)*SKS(MK(PMK)); }
			
		if(Mhs==MHS(PMK)) { JmlSKS+=SKS(MK(PMK)); nilai += GetNilaiInNumber(PMK)*SKS(MK(PMK)); }	
	}
	
	if(JmlSKS==0) {
		IPK(Mhs) = TABELMHS_UNDEF_VAL;
		JMLSKS(Mhs) = TABELMHS_UNDEF_VAL;
	}
	else {
		IPK(Mhs) = nilai/JmlSKS;
		JMLSKS(Mhs) = JmlSKS;
	}
}

bool 		EvaluateQueryTabelPMKPhase2(AddressPMK Mhs, Reader R, string compare, bool *query_succeed) {
	Reader temp = R;
	bool retval = false;
	if(IsStringEqual(temp.argument[0],Kol_NIM)) {
		if(IsStringEqual(compare,CharToString("<"))) {
			retval = IsStringLt(NIM(MHS(Mhs)),temp.argument[2]);
		}
		else {
			retval = IsStringEqual(NIM(MHS(Mhs)), temp.argument[2]);
		}
	}
	else if(IsStringEqual(temp.argument[0],Kol_KodeMK)) {
		if(IsStringEqual(compare,CharToString("<"))) {
			retval = IsStringLt(KODEMK(MK(Mhs)),temp.argument[2]);
		}
		else {
			retval = IsStringEqual(KODEMK(MK(Mhs)),temp.argument[2]);
		}
	}
	else if(IsStringEqual(temp.argument[0],Kol_Semester)) {
		if(!IsStringFloat(temp.argument[2])){
			StartPrintMsg(); printf("QUERY ERROR : %s bukan angka\n",temp.argument[2].s);
			*query_succeed = false;
		}
		else {
			if(IsStringEqual(compare,CharToString("<"))) {
				retval = SEMESTER(Mhs) < StringToInt(temp.argument[2]);
			}
			else {
				retval = SEMESTER(Mhs) == StringToInt(temp.argument[2]);
			}
		}
	}
	else if(IsStringEqual(temp.argument[0],Kol_Nilai)) {
		if(IsStringEqual(compare,CharToString("<"))) {
			char n[2]; sprintf(n, "%c", NILAI(Mhs));
			retval = IsStringLt(CharToString(n),UpperCase(temp.argument[2]));
		}
		else {
			char n[2]; sprintf(n, "%c", NILAI(Mhs));
			retval = IsStringEqual(CharToString(n),temp.argument[2]);
		}
	}
	else if(IsStringEqual(temp.argument[0],Kol_ThnAkademik)) {
		if(!IsStringThnAkademik(temp.argument[2])) {
			StartPrintMsg(); printf("QUERY ERROR : %s bukan tahun akademik\n",temp.argument[2].s);
			*query_succeed = false;
		}
		else {
			if(IsStringEqual(compare,CharToString("<"))) {
				retval = IsThnAkademikLT(TAHUN(Mhs),StringToThnAkademik(temp.argument[2]));
			}
			else {
				retval = IsThnAkademikEq(TAHUN(Mhs),StringToThnAkademik(temp.argument[2]));
			}
		}
	}
	else {
		StartPrintMsg(); printf("QUERY ERROR : Kolom tidak terdefinisi!\n");
		retval = false;
		*query_succeed = false;
	}
	return retval;
}

bool 		EvaluateQueryTabelPMK(AddressPMK PMK, Reader R, bool *query_succeed) {
	// exit jika argument < 3
	if(R.nArgument < 3) {
		StartPrintMsg(); printf("QUERY ERROR : Query tidak lengkap\n");
		*query_succeed = false;
		return false;
	}
	
	*query_succeed = true;

	Reader temp = R;
	bool retval;
	if(IsStringEqual(temp.argument[1],CharToString("<"))) {
		retval = EvaluateQueryTabelPMKPhase2(PMK, R, temp.argument[1], query_succeed);
	}
	else if(IsStringEqual(temp.argument[1],CharToString("<="))) {
		retval = EvaluateQueryTabelPMKPhase2(PMK, R, CharToString("<"), query_succeed) || EvaluateQueryTabelPMKPhase2(PMK, R, CharToString("="), query_succeed);
	}
	else if(IsStringEqual(temp.argument[1],CharToString(">="))) {
		retval = !EvaluateQueryTabelPMKPhase2(PMK, R, CharToString("<"), query_succeed);
	}
	else if(IsStringEqual(temp.argument[1],CharToString(">"))) {
		retval = !EvaluateQueryTabelPMKPhase2(PMK, R, CharToString("<"), query_succeed) && !EvaluateQueryTabelPMKPhase2(PMK, R, CharToString("="), query_succeed);
	}
	else if(IsStringEqual(temp.argument[1],CharToString("=="))) {
		retval = EvaluateQueryTabelPMKPhase2(PMK, R, CharToString("="), query_succeed);
	}
	else if(IsStringEqual(temp.argument[1],CharToString("!="))) {
		retval = !EvaluateQueryTabelPMKPhase2(PMK, R, CharToString("="), query_succeed);
	}
	else {
		StartPrintMsg(); printf("QUERY ERROR : Perintah relasional %s tidak dikenali!\n", temp.argument[1].s);
		retval = false;
		*query_succeed = false;
	}
	
	
	if(R.nArgument==3) { // basis  
		return retval;
	}
	else if(IsStringEqual(R.argument[3],CharToString("AND"))) { // rekursi
		DeleteArguments(&temp,0,4);
		return retval && EvaluateQueryTabelPMK(PMK, temp, query_succeed);
	}
	else if(IsStringEqual(R.argument[3],CharToString("OR"))) { // rekursi
		DeleteArguments(&temp,0,4);
		return retval || EvaluateQueryTabelPMK(PMK, temp, query_succeed);
	}
	else {
		StartPrintMsg(); printf("QUERY ERROR : Perintah relasional %s tidak dikenali!\n", temp.argument[3].s);
		*query_succeed = false;
		return false;
	}
}

void		DestroyTabelPMK(TabelPMK *T) {
	AddressPMK M;
	TRAVERSETOEND2(M, FIRST(*T)) {
		DestroyRecordPMK(M, &(*T));
	}
	*T = NULL;
}

// OUTPUT - for testing
void		PrintTabelPMK(TabelPMK T) {
	if(ISLINKEDLISTEMPTY(T)) {
		puts("-EMPTY-");
	}
	else {
		PrintTabelPMKHeader(true,true,true,true,true); puts("");
		AddressPMK M;
		TRAVERSETOEND(M,FIRST(T)) {
			PrintRecordPMK(M,true,true,true,true,true); puts("");
		}
		PrintRecordPMK(M,true,true,true,true,true); puts("");
	}
}

void		PrintTabelPMKHeader(bool showNIM, bool showKode, bool showSemester, bool TA, bool showNilai) {
	if(showNIM) {
		string s = Kol_NIM;
		printf("%-15s", s.s);
	}
	if(showKode) {
		string s = Kol_KodeMK;
		printf("%-15s", s.s);
	}
	if(showSemester) {
		string s = Kol_Semester;
		printf("%-10s", s.s);
	}
	if(TA) {
		string s = Kol_ThnAkademik;
		printf("%-15s", s.s);
	}
	if(showNilai) {
		string s = Kol_Nilai;
		printf("%-7s", s.s);
	}
}

void		PrintRecordPMK(AddressPMK PMK, bool showNIM, bool showKode, bool showSemester, bool TA, bool showNilai) {
	if(showNIM) printf("%-15s", NIM(MHS(PMK)).s);
	if(showKode) printf("%-15s", KODEMK(MK(PMK)).s);
	if(showSemester) printf("%-10d", SEMESTER(PMK));
	if(TA)  {
		string buff = ThnAkademikToString(TAHUN(PMK));
		printf("%-15s", buff.s);
	}
	if(showNilai) printf("%-7c", NILAI(PMK));
}
